#include <stdlib.h>
#include "common.h"
#include "arrays.h"
#include "commands.h"


/****************************************************************************
** GLOBAL VARIABLES
****************************************************************************/
extern arr_t movies_arr;
extern arr_t games_arr;
extern arr_t playlists_arr;

static const char * movie_genres[] = {"Comedy", "Drama", "Thriller", 
	"Horror", "Cartoon", "Fiction", "General", NULL};

static const char * music_genres[] = {"Rock", "Classic", "Metal", 
	"Israeli", "Jazz", "MTV", "General", NULL};


/****************************************************************************
** FUNCTION PROTOTYPES
****************************************************************************/
/* command handlers */
static success_t cmd_add_app(const char ** args);
static success_t cmd_remove_app(const char ** args);
static success_t cmd_edit_app(const char ** args);
static success_t cmd_add_usage(const char ** args);
static success_t cmd_add_score(const char ** args);
static success_t cmd_report_app(const char ** args);

/* specific report options */
static success_t cmd_report_best_player(game_t * game, int count);
static success_t cmd_report_unused_movies(void);
static success_t cmd_report_unused_games(void);
static success_t cmd_report_unused_playlists(void);
static success_t cmd_report_most_popular_movies(int count);
static success_t cmd_report_most_popular_games(int count);
static success_t cmd_report_most_popular_playlists(int count);

/* helpers */
static success_t contains(const char ** coll, const char * name);
static int cmp_scores(const player_t * a, const player_t * b);
static int cmp_movie_unused(const movie_t * a, const movie_t * b);
static int cmp_game_unused(const game_t * a, const game_t * b);
static int cmp_playlist_unused(const playlist_t * a, const playlist_t * b);
static int cmp_movie_mostused(const movie_t * a, const movie_t * b);
static int cmp_game_mostused(const game_t * a, const game_t * b);
static int cmp_playlist_mostused(const playlist_t * a, const playlist_t * b);


/****************************************************************************
** FUNCTION IMPLEMENTATION
****************************************************************************/
success_t cmd_dispatch(const char * cmd, const char ** args)
{
	if (strcmp(cmd, "AddApplication") == 0)
		return cmd_add_app(args);
	else if (strcmp(cmd, "RemoveApplication") == 0)
		return cmd_remove_app(args);
	else if (strcmp(cmd, "AddDetails") == 0)
		return cmd_edit_app(args);
	else if (strcmp(cmd, "UsedApplication") == 0)
		return cmd_add_usage(args);
	else if (strcmp(cmd, "AddScoreToGame") == 0)
		return cmd_add_score(args);
	else if (strcmp(cmd, "Report") == 0)
		return cmd_report_app(args);
	
	/* UNREPORTABLE ERROR: unknown command */
	return TRUE;
}

static success_t cmd_add_app(const char ** args)
{
	const char * type = args[0];
	const char * name = args[1];
	movie_t * the_movie;
	game_t * the_game;
	playlist_t * the_playlist;
	
	if (strcmp(type, "Movie") == 0)
	{
		ASSERT_ERROR(arr_find(&movies_arr, name) == -1, err_AppExists);
		the_movie = arr_append(&movies_arr);
		if (the_movie == NULL) 
			return FALSE;
		strcpy(the_movie->name, name);
		the_movie->usage = 0;
		the_movie->year = 0;
		strcpy(the_movie->genre, "General");
	}
	else if (strcmp(type, "Game") == 0)
	{
		ASSERT_ERROR(arr_find(&games_arr, name) == -1, err_AppExists);
		the_game = arr_append(&games_arr);
		if (the_game == NULL) 
			return FALSE;
		strcpy(the_game->name, name);
		the_game->usage = 0;
		the_game->end_year = 99;
		the_game->start_year = 0;
	}
	else if (strcmp(type, "PlayList") == 0)
	{
		ASSERT_ERROR(arr_find(&playlists_arr, name) == -1, err_AppExists);
		the_playlist = arr_append(&playlists_arr);
		if (the_playlist == NULL) 
			return FALSE;
		strcpy(the_playlist->name, name);
		the_playlist->usage = 0;
		the_playlist->songs = 0;
		strcpy(the_playlist->genre, "General");
	}
	else 
	{
		mtm_error(err_WrongAppType);
	}
	
	return TRUE;
}

static success_t cmd_remove_app(const char ** args)
{
	int i;
	const char * type = args[0];
	const char * name = args[1];
	
	if (strcmp(type, "Movie") == 0)
	{
		i = arr_find(&movies_arr, name);
		ASSERT_ERROR(i != -1, err_AppNotExists);
		if (!arr_remove(&movies_arr, i))
			return FALSE;
	}
	else if (strcmp(type, "Game") == 0)
	{
		i = arr_find(&games_arr, name);
		ASSERT_ERROR(i != -1, err_AppNotExists);
		if (!arr_remove(&games_arr, i))
			return FALSE;
	}
	else if (strcmp(type, "PlayList") == 0)
	{
		i = arr_find(&playlists_arr, name);
		ASSERT_ERROR(i != -1, err_AppNotExists);
		if (!arr_remove(&playlists_arr, i))
			return FALSE;
	}
	else 
	{
		mtm_error(err_WrongAppType);
	}
	
	return TRUE;
}

static success_t contains(const char ** coll, const char * name)
{
	int i;

	for (i = 0; coll[i] != NULL; i++)
	{
		if (strcmp(coll[i], name) == 0)
			return TRUE;
	}
	return FALSE;
}

static success_t cmd_edit_app(const char ** args)
{
    const char * type = args[0];
	const char * name = args[1];
    movie_t * the_movie;
	game_t * the_game;
	playlist_t * the_playlist;
	long i, j;
	
	if (strcmp(type, "Movie") == 0)
	{
		the_movie = arr_get(&movies_arr, name);
		ASSERT_ERROR(the_movie != NULL, err_AppNotExists);
		i = strtol(args[2], NULL, 10);
		ASSERT_ERROR(i >= 0,  err_WrongYear);
		ASSERT_ERROR(contains(movie_genres, args[3]),  err_WrongType);
		the_movie->year = i;
		strcpy(the_movie->genre, args[3]);
	}
	else if (strcmp(type, "Game") == 0)
	{
		the_game = arr_get(&games_arr, name);
		ASSERT_ERROR(the_game != NULL, err_AppNotExists);
		i = strtol(args[2], NULL, 10);
		j = strtol(args[3], NULL, 10);
		ASSERT_ERROR(i >= 0 && i <= 99 && j >= 0 && j <= 99 && i < j, 
			err_WrongAge);
		the_game->start_year = i;
		the_game->end_year = j;
	}
	else if (strcmp(type, "PlayList") == 0)
	{
		the_playlist = arr_get(&playlists_arr, name);
		ASSERT_ERROR(the_playlist != NULL, err_AppNotExists);
		i = strtol(args[2], NULL, 10);
		ASSERT_ERROR(i > 0, err_WrongNumOfSongs);
		ASSERT_ERROR(contains(music_genres, args[3]), err_WrongType);
		the_playlist->songs = i;
		strcpy(the_playlist->genre, args[3]);
	}
	else 
	{
		mtm_error(err_WrongAppType);
	}
	
	return TRUE;
}

static success_t cmd_add_usage(const char ** args)
{
	const char * type = args[0];
	const char * name = args[1];
	long usage = strtol(args[2], NULL, 10);
	movie_t * the_movie;
	game_t * the_game;
	playlist_t * the_playlist;
	
	if (strcmp(type, "Movie") == 0)
	{
		ASSERT_ERROR(usage >= 0, err_WrongNumOfUsers);
		the_movie = arr_get(&movies_arr, name);
		ASSERT_ERROR(the_movie != NULL, err_AppNotExists);
		the_movie->usage += usage;
	}
	else if (strcmp(type, "Game") == 0)
	{
		ASSERT_ERROR(usage >= 0, err_WrongNumOfUsers);
		the_game = arr_get(&games_arr, name);
		ASSERT_ERROR(the_game != NULL, err_AppNotExists);
		the_game->usage += usage;
	}
	else if (strcmp(type, "PlayList") == 0)
	{
		ASSERT_ERROR(usage >= 0, err_WrongNumOfUsers);
		the_playlist = arr_get(&playlists_arr, name);
		ASSERT_ERROR(the_playlist != NULL, err_AppNotExists);
		the_playlist->usage += usage;
	}
	else 
	{
		mtm_error(err_WrongAppType);
	}

	return TRUE;
}

static success_t cmd_add_score(const char ** args)
{
	const char * game_name = args[0];
	const char * player_name = args[1];
	long score = strtol(args[2], NULL, 10);
	player_t * the_player;
	game_t * the_game;

	the_game = arr_get(&games_arr, game_name);
	ASSERT_ERROR(the_game != NULL, err_AppNotExists);
	ASSERT_ERROR(score >= 0, err_WrongScore);
	
	the_player = arr_get(&the_game->players, player_name);
	if (the_player == NULL)
	{
		the_player = arr_append(&the_game->players);
		strcpy(the_player->name, player_name);
		the_player->score = score;
	}
	else
	{
		if (the_player->score < score)
			the_player->score = score;
	}
	
	return TRUE;
}

static success_t cmd_report_app(const char ** args)
{
	long n;
	const char * mode = args[0];
	const char * apptype;
	game_t * the_game;
	
	if (strcmp(mode, "MostPopular") == 0)
	{
		apptype = args[1];
		n = strtol(args[2], NULL, 10);
		if (strcmp(apptype, "Movie") == 0)
		{
			ASSERT_ERROR(n > 0, err_WrongN);
			return cmd_report_most_popular_movies(n);
		}
		else if (strcmp(apptype, "Game") == 0)
		{
			ASSERT_ERROR(n > 0, err_WrongN);
			return cmd_report_most_popular_games(n);
		}
		else if (strcmp(apptype, "PlayList") == 0)
		{
			ASSERT_ERROR(n > 0, err_WrongN);
			return cmd_report_most_popular_playlists(n);
		}
		else
		{
			mtm_error(err_WrongAppType);
		}
	}
	else if (strcmp(mode, "BestPlayer") == 0)
	{
		the_game = arr_get(&games_arr, args[1]);
		ASSERT_ERROR(the_game != NULL, err_AppNotExists);
		n = strtol(args[2], NULL, 10);
		ASSERT_ERROR(n > 0, err_WrongN);
		return cmd_report_best_player(the_game, n);
	}
	else if (strcmp(mode, "NotUsed") == 0)
	{
		apptype = args[1];
		if (strcmp(apptype, "Movie") == 0)
			return cmd_report_unused_movies();
		else if (strcmp(apptype, "Game") == 0)
			return cmd_report_unused_games();
		else if (strcmp(apptype, "PlayList") == 0)
			return cmd_report_unused_playlists();
		else
			mtm_error(err_WrongAppType);
	}
	else
	{
		mtm_error(err_WrongReport);
	}

	return TRUE;
}

static int cmp_scores(const player_t * a, const player_t * b)
{
	int diff = b->score - a->score;
	if (diff == 0)
		diff = strcmp(a->name, b->name);
	return diff;
}

static int cmp_movie_unused(const movie_t * a, const movie_t * b)
{
	int diff = a->usage - b->usage;
	if (diff == 0)
		return strcmp(a->name, b->name);
	return diff;
}

static int cmp_game_unused(const game_t * a, const game_t * b)
{
	int diff = a->usage - b->usage;
	if (diff == 0)
		return strcmp(a->name, b->name);
	return diff;
}

static int cmp_playlist_unused(const playlist_t * a, const playlist_t * b)
{
	int diff = a->usage - b->usage;
	if (diff == 0)
		return strcmp(a->name, b->name);
	return diff;
}

static int cmp_movie_mostused(const movie_t * a, const movie_t * b)
{
	int diff = b->usage - a->usage;
	if (diff == 0)
		return strcmp(a->name, b->name);
	return diff;
}

static int cmp_game_mostused(const game_t * a, const game_t * b)
{
	int diff = b->usage - a->usage;
	if (diff == 0)
		return strcmp(a->name, b->name);
	return diff;
}

static int cmp_playlist_mostused(const playlist_t * a, const playlist_t * b)
{
	int diff = b->usage - a->usage;
	if (diff == 0)
		return strcmp(a->name, b->name);
	return diff;
}

static success_t cmd_report_best_player(game_t * game, int count)
{
	int i;
	player_t * p;
	iterator_t iter;

	arr_sort(&game->players, (compare_t)cmp_scores);
	arr_getiter(&game->players, &iter);
	
	for (i = 0; i < count; i++)
	{
		p = arr_iternext(&iter);
		if (p == NULL)
			break;
		mtm_printScore(outstream, p->name, p->score);
	}
	
	return TRUE;
}

static success_t cmd_report_unused_movies(void)
{
	movie_t * m;
	iterator_t iter;
	
	arr_sort(&movies_arr, (compare_t)cmp_movie_unused);
	arr_getiter(&movies_arr, &iter);
	m = arr_iternext(&iter);
	
	while (m != NULL && m->usage == 0)
	{
		mtm_printMovie(outstream, m->name, m->year, m->genre, m->usage);
		m = arr_iternext(&iter);
	}
	
	return TRUE;
}

static success_t cmd_report_unused_games(void)
{
	game_t * g;
	iterator_t iter;
	
	arr_sort(&games_arr, (compare_t)cmp_game_unused);
	arr_getiter(&games_arr, &iter);
	g = arr_iternext(&iter);
	
	while (g != NULL && g->usage == 0)
	{
		mtm_printGame(outstream, g->name, g->start_year, g->end_year, 
			g->usage);
		g = arr_iternext(&iter);
	}
	
	return TRUE;
}

static success_t cmd_report_unused_playlists(void)
{
	playlist_t * p;
	iterator_t iter;
	
	arr_sort(&playlists_arr, (compare_t)cmp_playlist_unused);
	arr_getiter(&playlists_arr, &iter);
	p = arr_iternext(&iter);
	
	while (p != NULL && p->usage == 0)
	{
		mtm_printPlayList(outstream, p->name, p->songs, p->genre, p->usage);
		p = arr_iternext(&iter);
	}
	
	return TRUE;
}

static success_t cmd_report_most_popular_movies(int count)
{
	int i;
	movie_t * m;
	iterator_t iter;
	
	arr_sort(&movies_arr, (compare_t)cmp_movie_mostused);
	arr_getiter(&movies_arr, &iter);
	
	for (i = 0; i < count; i++)
	{
		m = arr_iternext(&iter);
		if (m == NULL)
			break;
		mtm_printMovie(outstream, m->name, m->year, m->genre, m->usage);
	}
	
	return TRUE;
}

static success_t cmd_report_most_popular_games(int count)
{
	int i;
	game_t * g;
	iterator_t iter;
	
	arr_sort(&games_arr, (compare_t)cmp_game_mostused);
	arr_getiter(&games_arr, &iter);
	
	for(i = 0; i < count; i++)
	{
		g = arr_iternext(&iter);
		if (g == NULL)
			break;
		mtm_printGame(outstream, g->name, g->start_year, g->end_year, 
			g->usage);
	}
	
	return TRUE;
}

static success_t cmd_report_most_popular_playlists(int count)
{
	int i;
	playlist_t * p;
	iterator_t iter;
	
	arr_sort(&playlists_arr, (compare_t)cmp_playlist_mostused);
	arr_getiter(&playlists_arr, &iter);
	
	for(i = 0; i < count; i++)
	{
		p = arr_iternext(&iter);
		if (p == NULL)
			break;
		mtm_printPlayList(outstream, p->name, p->songs, p->genre, p->usage);
	}
	
	return TRUE;
}

